package de.jmda.gen.java.naming;

import java.io.File;

import javax.lang.model.element.TypeElement;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.log4j.Logger;

import de.jmda.mproc.ProcessingUtilities;

public abstract class TargetPackageNameBuilder
{
	private final static Logger LOGGER =
			Logger.getLogger(TargetPackageNameBuilder.class);

	private final static File CLASS_PROPERTIES_FILE =
			new File(TargetPackageNameBuilder.class.getName() + ".properties");

	private   static final String SUFFIX_APPEND_KEY      = "SUFFIX_APPEND_KEY";
	private   static final String SUFFIX_APPEND_DEFAULT  = "impl";
	protected static final String SUFFIX_APPEND;

	private   static final String SUFFIX_REPLACE_KEY     = "SUFFIX_REPLACE_KEY";
	private   static final String SUFFIX_REPLACE_DEFAULT = "impl";
	protected static final String SUFFIX_REPLACE;

	static
	{
		// initialise constants from properties stored in CLASS_PROPERTIES_FILE
		PropertiesConfiguration configuration;

		try
		{
			configuration = new PropertiesConfiguration(CLASS_PROPERTIES_FILE);
		}
		catch (ConfigurationException e)
		{
			throw new ExceptionInInitializerError(
					"failure loading property configuration\n" + e);
		}

		// make sure we have a SUFFIX_APPEND_KEY  value in the class properties
		configuration.setProperty(SUFFIX_APPEND_KEY,  SUFFIX_APPEND_DEFAULT);
		// make sure we have a SUFFIX_REPLACE_KEY value in the class properties
		configuration.setProperty(SUFFIX_REPLACE_KEY, SUFFIX_REPLACE_DEFAULT);

		try
		{
			configuration.save(CLASS_PROPERTIES_FILE);
		}
		catch (ConfigurationException e)
		{
			LOGGER.warn("failure writing class properties file", e);
		}

		// now we have a valid SUFFIX_APPEND
		SUFFIX_APPEND  = configuration.getString(SUFFIX_APPEND_KEY);
		// now we have a valid SUFFIX_REPLACE
		SUFFIX_REPLACE = configuration.getString(SUFFIX_REPLACE_KEY);
	}

	/**
	 * @param typeElement
	 * @return generation target package name for <code>typeElement</code>
	 */
	public static String buildTargetPackageName(TypeElement typeElement)
	{
		// search for TargetPackageNamingInfo in classDeclaration
		TargetPackageNamingInfo targetPackageNamingInfo =
				typeElement.getAnnotation(TargetPackageNamingInfo.class);

		if (targetPackageNamingInfo == null)
		{
			// search for TargetPackageNamingInfo in typeDeclaration's
			// packageDeclaration
			targetPackageNamingInfo =
					ProcessingUtilities.getPackageOf(typeElement)
					                   .getAnnotation(TargetPackageNamingInfo.class);
		}

		if (targetPackageNamingInfo == null)
		{
			LOGGER.error(
					"no [" + TargetPackageNamingInfo.class.getName() +
					"], maybe annotated package-info.java is not passed to javac, " +
					"aborting build of target package name");
			return null;
		}

		String packageName = ProcessingUtilities.getPackageQualifiedName(typeElement);

		switch (targetPackageNamingInfo.packageNamingStrategy())
		{
			case QUALIFIED:
			{
				return targetPackageNamingInfo.packageNamingStrategyInfo();
			}
			case SUFFIX_APPEND_DEFAULT :
			{
				return packageName + "." + SUFFIX_APPEND;
			}
			case SUFFIX_APPEND_SPECIFIED :
			{
				return
						packageName + "." +
								targetPackageNamingInfo.packageNamingStrategyInfo();
			}
			case SUFFIX_REPLACE_DEFAULT :
			{
				return
						packageName.substring(0, packageName.lastIndexOf('.') + 1) +
								SUFFIX_REPLACE;
			}
			case SUFFIX_REPLACE_SPECIFIED :
			{
				return
						packageName.substring(0, packageName.lastIndexOf('.') + 1) +
								targetPackageNamingInfo.packageNamingStrategyInfo();
			}
			case SUFFIX_TRUNCATE :
			{
				return
						packageName.substring(0, packageName.lastIndexOf('.'));
			}
		}

		throw new AssertionError(
				"unknown package naming strategy [" +
						targetPackageNamingInfo.packageNamingStrategy() + "]");
	}

	public static String buildTargetPackageNameDAO(TypeElement typeElement)
	{
		return buildTargetPackageName(typeElement);
	}

	public static String buildTargetPackageNameDAOImplJPA(TypeElement typeElement)
	{
		return buildTargetPackageNameDAO(typeElement) + ".jpa";
	}

	public static String buildTargetPackageNameDAOServiceFactoryJPA(TypeElement typeElement)
	{
		return buildTargetPackageNameDAOImplJPA(typeElement);
	}

	public static String buildTargetPackageNameDAOServiceJPAImplAM(TypeElement typeElement)
	{
		return buildTargetPackageNameDAOImplJPA(typeElement) + ".am";
	}

	public static String buildTargetPackageNameDAOServiceJPAImplCMLocal(TypeElement typeElement)
	{
		return buildTargetPackageNameDAOImplJPA(typeElement) + ".cm";
	}

	public static String buildTargetPackageNameDAOServiceJPAImplCMRemote(TypeElement typeElement)
	{
		return buildTargetPackageNameDAOServiceJPAImplCMLocal(typeElement);
	}

	public static String buildTargetPackageNameDAOServiceJPAImplCMImpl(TypeElement typeElement)
	{
		return buildTargetPackageNameDAOServiceJPAImplCMLocal(typeElement);
	}

	public static String buildTargetPackageNameCRUDTest(TypeElement typeElement)
	{
		return buildTargetPackageName(typeElement) + ".test";
	}

	public static String buildTargetPackageNameRelationSupportTest(TypeElement typeElement)
	{
		return buildTargetPackageNameCRUDTest(typeElement);
	}
}